<html>
<head>
<meta charset="utf-8" />
<title>五子棋</title>
<style>
body{
    margin:0px;
}
.full{
    width: 100%;
    height: 100%;    
}
</style>
<script>

const BLACK_PIECE = 1;
const WHITE_PIECE = 2;

const ERROR_EXIST = -1;
const ERROR_RANGE = -2;
const ERROR_FINISH = -3;

const GRID_COUNT = 19;

let state_ = {};  

//重置棋子状态
function reset(state){
    state.piece = {};
    state.finish = false;
    state.step = BLACK_PIECE;
    state.last = null;    
    for (let i = 0; i < GRID_COUNT; i++) {
        state.piece[i] = {};
    }
}

//获取指定位置的棋子
function getPieceColor(state, x, y){
    if(x < 0 || x >= GRID_COUNT)return ERROR_RANGE;
    if(y < 0 || y >= GRID_COUNT)return ERROR_RANGE;
    let piece = state.piece[x][y];
    if(!piece)return 0;
    return piece.color;
}

//检查是否获胜, 获胜返回true
function checkFinish(state, x, y, step){
    let count = [0,0,0,0];
    let stop = [];
    let exp = step;
    let s = state;
    for (let i = 1; i <= 4; i++){
        if(!stop[0] && getPieceColor(s, x - i, y) == exp){count[0] ++;}else{stop[0]=1;};
        if(!stop[1] && getPieceColor(s, x + i, y) == exp){count[0] ++;}else{stop[1]=1;};
        if(count[0] >= 4)return true;
        if(!stop[2] && getPieceColor(s, x, y - i) == exp){count[1] ++;}else{stop[2]=1;};
        if(!stop[3] && getPieceColor(s, x, y + i) == exp){count[1] ++;}else{stop[3]=1;};
        if(count[1] >= 4)return true;
        if(!stop[4] && getPieceColor(s, x - i, y - i) == exp){count[2] ++;}else{stop[4]=1;};
        if(!stop[5] && getPieceColor(s, x + i, y + i) == exp){count[2] ++;}else{stop[5]=1;};
        if(count[2] >= 4)return true;
        if(!stop[6] && getPieceColor(s, x - i, y + i) == exp){count[3] ++;}else{stop[6]=1;};
        if(!stop[7] && getPieceColor(s, x + i, y - i) == exp){count[3] ++;}else{stop[7]=1;};
        if(count[3] >= 4)return true;
    }
    return false;
}

//下子
function putPiece(state, x, y){
    let step = state.step;
    if(state.finish)return ERROR_FINISH;
    if(x < 0 || x >= GRID_COUNT)return ERROR_RANGE;
    if(y < 0 || y >= GRID_COUNT)return ERROR_RANGE;
    if(state.piece[x][y])return ERROR_EXIST;
    let piece = {};
    piece.color = step;
    if(state.last){
        piece.prev = {};
        piece.prev.x = state.last.x;
        piece.prev.y = state.last.y;
    }
    state.piece[x][y] = piece;
    state.last = {};
    state.last.x = x;
    state.last.y = y;
    if(checkFinish(state, x, y, step)){
        state.finish = true;
        return 0;
    }
    state.step = (step == WHITE_PIECE)? BLACK_PIECE: WHITE_PIECE;
    return 0;
}

//悔棋
function retractPiece(state){
    if(!state.last)return;
    state.finish = false;
    let x = state.last.x;
    let y = state.last.y; 
    let piece = state.piece[x][y];
    state.piece[x][y] = undefined;
    if(piece.prev){
        state.last = {}
        state.last.x = piece.prev.x;
        state.last.y = piece.prev.y;
    }else{
        state.last = undefined;
    }
    state.step = (state.step == WHITE_PIECE)?BLACK_PIECE:WHITE_PIECE;
}


//遍历所有棋子 callback(state, x, y, color)
function forEachPiece(state, callback){
    let last = state.last;
    while(last){
        let x = last.x;
        let y = last.y;
        let piece = state.piece[x][y];
        callback(state,x,y,piece.color);
        last = piece.prev;
    }
}

//棋盘坐标转屏幕坐标
function posBoardToWin(state, x, y){
    let firstx = state.board.x + state.board.padding;
    let firsty = state.board.y + state.board.padding;
    let pos = {};
    pos.x = firstx + x * state.board.grid;
    pos.y = firsty + y * state.board.grid;
    return pos;
}

//屏幕坐标转棋盘坐标
function posWinToBoard(state, x, y){
    let firstx = state.board.x + state.board.padding;
    let firsty = state.board.y + state.board.padding;
    let disx = x - firstx;
    let disy = y - firsty;
    let pos = {};
    pos.x = Math.round(disx / state.board.grid);
    pos.y = Math.round(disy / state.board.grid);
    return pos;
}


//画单个棋子基础实现
function renderPieceBase(ctx, x, y, radius, color, shadow){
    //主背景 
    let r1 = radius * 0.98;
    ctx.fillStyle = color;
    ctx.shadowBlur = radius * 0.25;
    ctx.shadowColor = shadow?shadow:"black";
    ctx.beginPath();
    ctx.arc(x,y,r1,0,2*Math.PI);
    ctx.fill(); 
    ctx.shadowBlur = 0;    
    //高亮效果1
    let xoff = x + radius * 0.1;
    let yoff = y - radius * 0.2;
    let grd1 = ctx.createRadialGradient(xoff,yoff,radius*0.75,xoff,yoff,radius*1.5);
    grd1.addColorStop(0,color);
    grd1.addColorStop(1,"white");   
    ctx.fillStyle = grd1;    
    ctx.beginPath();
    ctx.arc(x,y,radius * 0.95,0,2*Math.PI);
    ctx.fill();
    //高亮效果2
    let r = radius * 0.8;
    ctx.lineWidth = radius * 0.15;
    ctx.strokeStyle = "white"
    ctx.globalAlpha = 0.8;  
    ctx.beginPath();
    ctx.arc(x,y,r,1.7*Math.PI,1.85*Math.PI);
    ctx.stroke(); 
    ctx.beginPath();
    ctx.arc(x,y,r,1.6*Math.PI,1.65*Math.PI);
    ctx.stroke();     
    ctx.globalAlpha = 1;
}


//画单个棋子, 按窗口坐标
function renderOnePieceWinPos(state,winx,winy,color){
    let ctx = state.ctx;
    let grid = state.board.grid;
    let radius = Math.floor(grid / 2) - 1;
    //let x = winx - radius;
    //let y = winy - radius; 
    //let img = (color==WHITE_PIECE)?img_white_piece:img_black_piece;
    //ctx.drawImage(img,0,0,36,36,x,y,radius*2,radius*2);
    let colorstr = (color==WHITE_PIECE)?"#cfcfcf":"#000000";
    let shadowstr = (color==WHITE_PIECE)?"#9c9c9c":"#000000";
    renderPieceBase(ctx,winx,winy,radius*0.9, colorstr, shadowstr);
}

//画单个棋子
function renderOnePiece(state,x,y,color){
    if(state.moving && state.moving.piece.x == x && state.moving.piece.y == y){
        //该棋子在移动
        return;
    }
    let winpos = posBoardToWin(state, x, y);
    renderOnePieceWinPos(state, winpos.x, winpos.y, color);
}

//画棋盘
function renderBoard(state){
    let x = state.board.x;
    let y = state.board.y;
    let width = state.board.width;
    let padding = state.board.padding;    
    let ctx = state.ctx;    
    let gridWidth = state.board.grid;
    let lineLength = gridWidth * 18;
    //背景
    ctx.fillStyle = "#EBBE82";
    ctx.fillRect(x, y, width, width);    
    //横线
    ctx.lineCap = "square";
    ctx.lineWidth = 1;
    ctx.strokeStyle = "black"
    let gridx = x + padding;
    let gridy = y + padding;
    for (let i = 0; i < 19; i++) {
        ctx.beginPath();
        ctx.moveTo(gridx, gridy);
        ctx.lineTo(gridx + lineLength, gridy);
        ctx.stroke();
        gridy += gridWidth;
    }
    //竖线
    gridx = x + padding;
    gridy = y + padding;
    for (let i = 0; i < 19; i++) {
        ctx.beginPath();
        ctx.moveTo(gridx, gridy);
        ctx.lineTo(gridx, gridy + lineLength);
        ctx.stroke();
        gridx += gridWidth;
    }
}

//绘制边缘空白
function rendetEdge(state){
    let ctx = state.ctx;
    ctx.fillStyle = "#000000";
    if(state.board.x){        
        ctx.fillRect(0, 0, state.board.x, state.canvas.height);
        ctx.fillRect(state.board.x + state.board.width, 0, state.board.x, state.canvas.height);       
    }
    if(state.board.y){        
        ctx.fillRect(0, 0, state.canvas.width, state.board.y);
        ctx.fillRect(0, state.board.y + state.board.width, state.canvas.width, state.board.y);       
    } 
    if(state.closing){
        //正在拖动屏幕边缘，边缘把棋盘覆盖
        let disx = state.closing.x - state.closing.start.x;
        let disy = state.closing.y - state.closing.start.y;
        if(state.closing.side == -1 && state.closing.x > state.board.x){
            ctx.fillRect(state.board.x, 0, state.closing.x - state.board.x, state.canvas.height);
        } 
        if(state.closing.side == 1 && state.closing.x < (state.board.x + state.board.width)){
            ctx.fillRect(state.closing.x, 0, state.board.x + state.board.width - state.closing.x, state.canvas.height);
        }
        if(state.closing.side == -2 && state.closing.y > state.board.y){
            ctx.fillRect(0, state.board.y, state.canvas.width, state.closing.y - state.board.y);
        } 
        if(state.closing.side == 2 && state.closing.y < (state.board.y + state.board.height)){
            ctx.fillRect(0, state.closing.y, state.canvas.width, state.board.y + state.board.height - state.closing.y);
        }                         
    }   
}

//全部绘制
function render(state){
    renderBoard(state);
    forEachPiece(state, renderOnePiece);
    rendetEdge(state);
    if(state.moving){
        renderOnePieceWinPos(state, state.moving.x, state.moving.y, state.moving.piece.color);
    }
}


//点击边缘空白区域检测，返回 -1=左边 1=右边 -2=上边 2=下边
function checkOutSide(state, x, y){
    if(state.canvas.width > state.canvas.height){
        if(x < state.board.x)return -1;
        if(x > (state.board.x + state.board.width))return 1;
    } else {
        if(y < state.board.y)return -2;
        if(y > (state.board.y + state.board.height))return 2;        
    }
    return 0;
}

//按下事件处理
function onMouseDown(state, x, y){    
    let side = checkOutSide(state, x, y);
    if(side){
        state.closing = {};
        state.closing.side = side;
        state.closing.start = {x:x, y:y};
        state.closing.x = x;
        state.closing.y = y;
        return;
    }
    let pos = posWinToBoard(state, x, y);
    let color = getPieceColor(state, pos.x, pos.y);
    if(color > 0){
        state.moving = {};
        state.moving.piece = {};
        state.moving.piece.x = pos.x;
        state.moving.piece.y = pos.y;
        state.moving.piece.color = color;
        state.moving.x = x;
        state.moving.y = y;   
        return;     
    }
    let ret = putPiece(state, pos.x, pos.y);
    if(ret < 0){        
        return;
    }
    renderOnePiece(state, pos.x, pos.y, getPieceColor(state, pos.x, pos.y));
    audio_chess.play();
}

//放开事件处理
function onMouseUp(state, x, y){
    let update = false;   
    let close = false; 
    if(state.moving){
        if(state.moving.piece.x == state.last.x && state.moving.piece.y == state.last.y){
            //正在移动最后一个，则悔棋
            retractPiece(state);
            let pos = posWinToBoard(state, x, y);
            let ret = putPiece(state, pos.x, pos.y);
            if(ret == ERROR_EXIST){ //移动到已有位置，则下回去
                putPiece(state, state.moving.piece.x, state.moving.piece.y);
            }
        }else{
            //非最后一个, 移出范围外，则重开
            if(checkOutSide(state,x,y) != 0){
                reset(state);
            }
        }
        update = true;
    }
    if(state.closing){
        //正在拖动边缘区域，判断是否全覆盖棋盘，覆盖则退出
        let side = checkOutSide(state, x, y);
        if((0 - side) == state.closing.side){
            close = true;
        }
        update = true;
        if(close)reset(state);
    }
    state.moving = null;
    state.closing = null;
    if(update){
        render(state);        
    }
    if(close){
        window.close();
    }
}

//拖动事件处理
function onMouseMove(state, x, y){
    if(state.moving){
        state.moving.x = x;
        state.moving.y = y;
        render(state);
    }else if(state.closing){
        state.closing.x = x;
        state.closing.y = y;
        render(state);
    }
}

//按body大小创建画布
function createCanvas(){
    let body = container;
    let canvas = document.createElement('canvas'); 
    let rc = body.getBoundingClientRect();
    canvas.width = rc.width;
    canvas.height = rc.height;    
    canvas.style.width = "100%";
    canvas.style.height = "100%";  
    body.innerHTML = '';
    body.appendChild(canvas);
    let ctx = canvas.getContext('2d');
    ctx.fillStyle = "#000000";
    ctx.fillRect(0,0,rc.width,rc.height);
    return canvas;
}

//按画布大小更新棋盘坐标、大小等
function updateSize(state){
    let w = state.canvas.width;
    let h = state.canvas.height;
    state.board = {};
    if(w > h){
        state.board.width = h;
        state.board.x = Math.round((w - h) / 2);
        state.board.y = 0;
    }else{
        state.board.width = w;
        state.board.x = 0;
        state.board.y = Math.round((h - w) / 2);
    }   
    state.board.height = state.board.width; 
    let center_x = Math.round(w / 2);
    let center_y = Math.round(h / 2);
    let grid_width = Math.round(state.board.width / 20);
    state.board.grid = grid_width;
    state.board.padding = Math.round((state.board.width - grid_width * 18) / 2);
}

function setEventListener(state, div){
    let opt = {passive: false};
    
    div.addEventListener('mousedown', (ev)=>{
        onMouseDown(state, ev.x, ev.y);
    });
    div.addEventListener('mouseup', (ev)=>{
        onMouseUp(state, ev.x, ev.y);
    });  
    div.addEventListener('mousemove', (ev)=>{
        onMouseMove(state, ev.x, ev.y);
    }); 
    div.addEventListener('mouseleave', (ev)=>{
        onMouseUp(state, -1, -1);
    });
    div.addEventListener('touchstart', (ev)=>{
        let x = ev.touches[0].pageX;
        let y = ev.touches[0].pageY; 
        onMouseDown(state, x, y); 
        ev.preventDefault();       
    },opt);
    div.addEventListener('touchmove', (ev)=>{
        let x = ev.touches[0].pageX;
        let y = ev.touches[0].pageY; 
        onMouseMove(state, x, y);
        ev.preventDefault();  
    },opt);
    div.addEventListener('touchend', (ev)=>{        
        let x = ev.changedTouches[0].pageX;
        let y = ev.changedTouches[0].pageY;        
        onMouseUp(state, x, y); 
        ev.preventDefault(); 
    },opt);
    div.addEventListener('touchcancel', (ev)=>{
        onMouseUp(state, -1, -1);
        ev.preventDefault(); 
    },opt);    
}

function createState(){
    let state = {};
    state.canvas = createCanvas(); 
    state.ctx = state.canvas.getContext('2d');
    updateSize(state);
    reset(state);
    renderBoard(state);
    setEventListener(state, container);
    return state;
}

function init(){   
    //disableTouchMove(); 
    let state = createState();
    window.onresize = function(){
        state.canvas = createCanvas(); 
        state.ctx = state.canvas.getContext('2d');        
        updateSize(state);
        render(state);
    }
}   

</script>
</head>
<body onload="init()">
<div id="container" class="full" ></div>
<audio id="audio_chess" style="display:none;" src="data:audio/mp3;base64,
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
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqoJZoaIeI34AAAI6ucnA+4bEwEi8VyJf4FY8LrNCsjc
ZDtjf6o6b6WinZkG5ub2bgBpCkDwTEUeYZb4u+TTWq+SwH//////////////4rtL//tQZOQBMk8N
1fnpGxobwAocAAABBlhVT+ekZyAjACiQAAAEXZ3Vk+hMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqC7u6oAAAAAAVoIEYCIm8
QpTi9QRUxMgAACBmQMk8uWWQj//////////////7UGTWAfFrC1J56DMKAKAJ4AAAAQVIZ0fsMEVo
AYBoQBAABP////6aTEFNRTMuOTkuNaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqr/+1Bk04PxTgzR+wwY2gBgGgAEAAECyCFdxTABaE6AZYAQAACqBkQRlUxBTUUzLjk5
LjVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//tQZMGD8JYF1XGp
SAoAYBnQBAABAbAPT8aEwCg7gKXAIAAEVQREFslMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VQjIJqgAMcAwsZfuN//////////////V/f/7UGSrj/AMANICIAAIAGAZ8AQAAQAsA1AGgAAgAYBn
gBAABN3uV6tSTEFNRTMuOTkuNaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqBwHAgFAYAAgDAAAAAABgKgwLQJSEL+eG
+BbPy+b+BCcz/8oGAf/AgYB+gAAAABIRAMBQKBQIBAAAAAAAmgAbeABUFt8FpCe+IICl4CwgQbfx
EkQ+Fbz/+1Bkq4/wDADTAaAACABgGdAEAAEAMANKB4AAIAGAZ0AQAAQVCR71VUxBTUUzLjk5LjVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
TEFNRTMuOTkuNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//tQZLgH8AsA04GgAAgA
YBmgBAABASQPU9TAACBHAKWChAAAVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/7UGTXAAFODFDuVKAABiAZ1cCAAAVISz+Y1QAAAAA/wwAA
AFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
VVX/+xBk3Y/wAAB/hwAACAAAD/DgAAEAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQ==
" ></audio>
</body>
</html>
